package com.karateca.columbiaimports.io;

import com.karateca.columbiaimports.ImportBean;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class writes the downloaded data to a file. The file contains the columns
 * for the DUA report, the DUA B, and the individual series.
 * @author Andres Dominguez
 */
public class ImportFile
{
    /** Output stream. */
    private PrintWriter out;
    /** Apache log */
    private static final Log log = LogFactory.getLog(ImportFile.class);
    /** Count the number of lines printed */
    private int linesPrinted;
    /** Number of imports printed */
    private int beansPrinted;
    /** Country code that will be written */
    private String country;
    /** Start date of the file */
    private String startDate;
    /** End date of the file */
    private String endDate;
    /** Directory where file will be written */
    private String fileDir;
    /** DUA header column names. */
    private static final String[] duaHeaderColumns =
    {
        "PAIS",
        "DECLARACION",
        "FECHA NUMERACION",
        "SUJETO A",
        "1.1 ADUANA",
        "1.1 CODIGO ADUANA",
        "1.2 CODIGO DECLARANTE",
        "1.2 DECLARANTE",
        "1.3 CODIGO IMPORTADOR",
        "1.3 IMPORTADOR",
        "1.5 DIRECCION",
        "3.1 MONEDA",
        "3.2 FORMA DE PAGO",
        "3.3 PLAZO",
        "3.4 ENT.FINANC.",
        "3.5 FEC.CARTA CRED",
        "3.6 EXONERACION C.I.",
        "4.1 MANIFIESTO",
        "4.2 MODO",
        "4.3 EMPRESA",
        "4.4. FEC.LLEGADA",
        "5.1. CODIGO TERMINAL",
        "5.1. TERMINAL",
        "5.5 TOTAL PESO NETO (KG)",
        "5.6 TOTAL PESO BRUTO(KG)",
        "5.7 TOTAL CANTIDAD BULTOS",
        "5.8 TOTAL UNIDADES FISICAS",
        "5.9 UNIDADES COMERCIALES",
        "5.10 RES.EXO.NUM.",
        "5.12 TOTAL SERIES",
        "5.13 TIPO TRATAMIENTO",
        "6.1 FOB MONEDA TRANSACCION",
        "6.1 FOB MONEDA TOTAL DOLARES",
        "6.2 FLETE MONEDA TRANSACCION",
        "6.2 FLETE MONEDA TOTAL DOLARES",
        "6.4 SEGURO MONEDA TRANSACCION",
        "6.4 SEGURO MONEDA TOTAL DOLARES",
        "6.5 CIF MONEDA TRANSACCION",
        "6.5 CIF MONEDA TOTAL DOLARES",
        "3.1 AD/VALOREM CANTIDAD A PAGAR",
        "3.1 AD/VALOREM LIBERACION",
        "3.1 AD/VALOREM LIQUIDACION",
        "3.2 DERECHO ESPECIFICO CANTIDAD A PAGAR",
        "3.2 DERECHO ESPECIFICO LIBERACION",
        "3.2 DERECHO ESPECIFICO LIQUIDACION",
        "3.3 IMP. SELECTIVO CONSUMO CANTIDAD A PAGAR",
        "3.3 IMP. SELECTIVO CONSUMO LIBERACION",
        "3.3 IMP. SELECTIVO CONSUMO LIQUIDACION",
        "3.4 IMP. PROMOCION MUNICIPAL CANTIDAD A PAGAR",
        "3.4 IMP. PROMOCION MUNICIPAL LIBERACION",
        "3.4 IMP. PROMOCION MUNICIPAL LIQUIDACION",
        "3.5 IMP. GENERAL A LA VENTA CANTIDAD A PAGAR",
        "3.5 IMP. GENERAL A LA VENTA LIBERACION",
        "3.5 IMP. GENERAL A LA VENTA LIQUIDACION",
        "3.6 DERECHO ANTIDUMPING CANTIDAD A PAGAR",
        "3.6 DERECHO ANTIDUMPING LIBERACION",
        "3.6 DERECHO ANTIDUMPING LIQUIDACION",
        "3.7 TASA SERVICIO DESPACHO CANTIDAD A PAGAR",
        "3.7 TASA SERVICIO DESPACHO LIBERACION",
        "3.7 TASA SERVICIO DESPACHO LIQUIDACION",
        "3.8 RECARGO NUMERACION CANTIDAD A PAGAR",
        "3.8 RECARGO NUMERACION LIBERACION",
        "3.8 RECARGO NUMERACION LIQUIDACION",
        "3.9 SOBRETASA ADICIONAL CANTIDAD A PAGAR",
        "3.9 SOBRETASA ADICIONAL LIBERACION",
        "3.9 SOBRETASA ADICIONAL LIQUIDACION",
        "3.11 TOTAL LIQUIDACION",
        "ULTIMO DIA DE PAGO",
        "FECHA CANCELACION",
        "BANCO DE CANCELACION"
    };
    /** Column names for DUA B. */
    private static final String[] duaBColumns =
    {
        "-Formato B-",
        "3.1 Nombre / Razon social",
        "3.2 Condicion",
        "3.3 Codigo",
        "3.3 Direccion",
        "3.4 Ciudad",
        "3.5 Pais",
        "3.6 Fax",
        "3.7 Telefono",
        "3.8 Pagina Web",
        "3.9 E-mail",
        "7.1",
        "7.2",
        "7.3",
        "7.4",
        "7.5",
        "7.6",
        "7.7",
        "7.8",
        "7.9",
        "7.10",
        "7.11",
        "7.12",
        "7.13",
        "7.14",
        "7.15"
    };
    /** Column names for series */
    private static final String[] seriesColumns =
    {
        "-Series-",
        "SERIE",
        "PUERTO EMBARQUE",
        "GUIA AEREA O B/L",
        "FEC.EMB.",
        "DECL.PREC.",
        "ITEM",
        "FEC.VCTO",
        "CERT.ORIGEN",
        "ESTADO",
        "CANT. BULTOS",
        "CLASE",
        "UNIDADES FISICAS U.F.",
        "PESO NETO (KG)",
        "PESO BRUTO (KG)",
        "MONEDA TRANSC.",
        "FOB US $",
        "FLETE",
        "SEGURO",
        "ADV $",
        "IGV $",
        "IPM $",
        "ISC $",
        "PAIS ORIGEN",
        "PAIS ADQUISICION",
        "TRATO PREF.INT.",
        "TRATO PREF. NAC.",
        "COD. LIBER.",
        "CERT.INSPECC.1",
        "CERT.INSPECC.2",
        "CERT.INSPECC.3",
        "No. FACTURA 1",
        "No. FACTURA 2",
        "No. FACTURA 3",
        "NANDINA",
        "DESCRIPCION DE PARTIDA ARANCELARIA",
        "NABANDINA",
        "TIPO MRGN",
        "UNIDADES COMERCIALES",
        "TIPO U.C.",
        "DESCRIPCION DE MERCANCIAS",
        "URL"
    };

    /**
     * Creates a new instance of ImportFile.
     * @param fileDir directory where files will be read and written.
     * @param country country code.
     * @param startDate start date of the file.
     * @param endDate end date of the file.
     */
    public ImportFile(String fileDir, String country, String startDate, String endDate)
    {
        this.fileDir = fileDir;
        this.country = country;
        this.startDate = startDate;
        this.endDate = endDate;
        this.linesPrinted = 0;
        this.beansPrinted = 0;
    }

    /**
     * Open temporary country file. A temporary file has the .temp extension.
     * @param append true if file will be appended.
     * @throws FileNotFoundException in case of error.
     */
    public void openTempFile(boolean append) throws FileNotFoundException
    {
        String tempFilePath;
        FileOutputStream fos;

        // Open file
        tempFilePath = getTempFileName();
        log.info("opening file " + tempFilePath);
        fos = new FileOutputStream(tempFilePath, append);
        out = new PrintWriter(fos);
    }

    /**
     * Rename the file from temp to final name. The file has to be renamed
     * when all imports have been downloaded and written.
     * @throws Exception in case of error.
     */
    public void markComplete() throws Exception
    {
        String tempPath;
        String finalPath;

        tempPath = getTempFileName();
        finalPath = getFileName();

        // rename file
        renameFile(tempPath, finalPath);
    }

    /**
     * Check if a temporary file exists for the country.
     * @return true when the temporary file exists.
     */
    public boolean tempFileExists()
    {
        File file;
        String tempFileName;
        boolean result;

        tempFileName = getTempFileName();
        file = new File(tempFileName);
        result = file.exists();
        log.info("File " + tempFileName + " exists: " + result);

        return result;
    }

    /**
     * Write the first line of the file. The header contains the column names.
     */
    public void writeColumnNames()
    {
        StringBuilder headerColumns;

        // Append all column names
        headerColumns = new StringBuilder();
        // DUA header
        for (String columnName : duaHeaderColumns)
        {
            headerColumns.append(columnName + "\t");
        }
        // DUA B
        for (String columnName : duaBColumns)
        {
            headerColumns.append(columnName + "\t");
        }
        // Series
        for (String columnName : seriesColumns)
        {
            headerColumns.append(columnName + "\t");
        }

        out.println(headerColumns.toString());
    }

    /**
     * Read the list of links that are present in the temporary file.
     * @return a set of links contained in the temp file.
     * @throws Exception in case of error.
     */
    public Set<String> readTempFileLinks() throws Exception
    {
        FileReader fileReader;
        BufferedReader reader;
        Set<String> tempFileLinks;
        String tempFileName;
        String link;
        String line;
        String[] cols;
        boolean firstLine = true;

        tempFileLinks = new HashSet<String>();
        // Open file and read all the links (last columns)
        tempFileName = getTempFileName();
        log.info("Reading links in temp file " + tempFileName);
        fileReader = new FileReader(tempFileName);
        reader = new BufferedReader(fileReader);
        line = reader.readLine();
        while (line != null)
        {
            // skip first line
            if (firstLine == false)
            {
                // Split line and get last column
                cols = line.split("\t");
                link = cols[cols.length - 1];
                tempFileLinks.add(link);
            }
            firstLine = false;
            line = reader.readLine();
        }
        reader.close();
        log.info("Found " + tempFileLinks.size() + " links in temp file");
        return tempFileLinks;
    }

    /**
     * Write an import bean to file. A line will be written by each individual
     * serie in the bean.
     * @param bean bean containing the data.
     * @param country country of the import.
     */
    public void writeBean(ImportBean bean, String country)
    {
        // Write one line per serie
        StringBuilder header;
        StringBuilder formatB;

        StringBuilder line;

        String columnName;

        this.beansPrinted++;

        // Header
        header = new StringBuilder();
        header.append(country + "\t");
        for (int i = 1; i < duaHeaderColumns.length; i++)
        {
            columnName = duaHeaderColumns[i];
            header.append(bean.getDuaHeader().get(columnName) + "\t");
        }

        // Format B
        formatB = new StringBuilder();
        formatB.append("-Formato B-\t");
        for (int i = 1; i <
                duaBColumns.length; i++)
        {

            columnName = duaBColumns[i];
            formatB.append(bean.getDuaB().get(columnName) + "\t");
        }

        // Get each of the series, create line and print
        for (Map<String, String> serie : bean.getSeries())
        {
            this.linesPrinted++;
            line =
                    new StringBuilder();
            line.append(header);
            line.append(formatB);
            line.append("-Series-\t");
            // Ignore last element because it is the URL
            for (int i = 1; i <
                    (seriesColumns.length - 1); i++)
            {
                columnName = seriesColumns[i];
                line.append(serie.get(columnName) + "\t");
            }

            line.append(bean.getLink());

            out.println(line.toString());
        }

    }

    /**
     * Close file.
     */
    public void closeFile()
    {
        log.info("Wrote " + this.beansPrinted + " links and " + this.linesPrinted + " series");
        out.close();
    }

    /**
     * Rename a file.
     * @param oldPath olf file name.
     * @param newPath new file name.
     * @throws Exception in case of error.
     */
    private void renameFile(String oldPath, String newPath) throws
            Exception
    {
        File file;

        log.info("Renaming file from " + oldPath + " to " + newPath);
        file = new File(oldPath);
        if (file.exists() == false)
        {
            throw new Exception("File " + oldPath + " does not exist");
        }
        file.renameTo(new File(newPath));
    }

    /**
     * Create the file name with the following pattern: country code _
     * start date _ end date.
     * @return the name of the file that will be created.
     */
    public String getFileName()
    {
        String fileName;
        String startD;
        String endD;

        startD = this.startDate.replaceAll("/", "-");
        endD = this.endDate.replaceAll("/", "-");
        // Create file name
        fileName = this.fileDir + country + "_" + startD + "_" + endD + ".csv";

        return fileName;
    }

    /**
     * Generate the name of the temporary file. It is the same name of the file
     * with the .temp extension.
     * @return the path of the temporary file.
     */
    public String getTempFileName()
    {
        return getFileName() + ".temp";
    }
}
